package com.whfc.common.vs.imou;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.whfc.common.exception.BizException;
import com.whfc.common.result.Result;
import com.whfc.common.result.ResultEnum;
import com.whfc.common.util.DateUtil;
import com.whfc.common.util.HttpUtil;
import com.whfc.common.vs.imou.constant.ImouConstant;
import com.whfc.common.vs.imou.entity.ImouChannelInfo;
import com.whfc.common.vs.imou.entity.ImouDeviceInfo;
import com.whfc.common.vs.imou.entity.ImouStreamInfo;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 大华乐橙视频监控API
 *
 * @author qzexing
 * @version 1.0
 * @date 2021-04-26 09:47
 */
public class ImouApi {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private String appKey;

    private String appSecret;

    public ImouApi(String appKey, String appSecret) {
        this.appKey = appKey;
        this.appSecret = appSecret;
    }

    public String getAppKey() {
        return appKey;
    }

    public String getAccessToken() {
        Map<String, Object> map = paramsInit();
        try {
            String response = HttpUtil.doPost(ImouConstant.GET_TOKEN_URL, JSON.toJSONString(map));
            logger.info("大华乐橙获取AccessToken,response:{}", response);
            Result result = parseResult(response);
            if (result == null || !ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                String msg = "大华乐橙获取Token失败";
                if (result != null && StringUtils.isNotEmpty(result.getMsg())) {
                    msg = result.getMsg();
                }
                throw new BizException(ResultEnum.FAILURE.getCode(), msg);
            }
            String token = ((JSONObject) result.getData()).getString("accessToken");
            if (StringUtils.isNotEmpty(token)) {
                return token;
            }
        } catch (Exception e) {
            logger.error("大华乐橙获取AccessToken出错", e);
        }
        return null;
    }


    /**
     * 绑定设备
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     * @param code         设备验证码
     */
    public void createDevice(String token, String deviceSerial, String code) {
        Map<String, Object> params = new HashMap<>(3);
        params.put("token", token);
        params.put("deviceId", deviceSerial);
        params.put("code", code);

        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String response = HttpUtil.doPost(ImouConstant.CREATE_DEVICE_URL, JSON.toJSONString(map));
            logger.info("创建设备,response:{}", response);
            Result result = parseResult(response);
            if (result == null || !ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                String msg = "新增设备失败";
                if (result != null && StringUtils.isNotEmpty(result.getMsg())) {
                    msg = result.getMsg();
                }
                throw new BizException(ResultEnum.FAILURE.getCode(), msg);
            }
            logger.info("添加大华设备成功，deviceSerial:{}", deviceSerial);
        } catch (Exception e) {
            logger.error("创建设备出错", e);
            throw new BizException(ResultEnum.FAILURE.getCode(), "新增大华设备失败.");
        }
    }

    /**
     * 解绑设备
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     */
    public void delDevice(String token, String deviceSerial) {
        Map<String, Object> params = new HashMap<>(2);
        params.put("token", token);
        params.put("deviceId", deviceSerial);

        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String response = HttpUtil.doPost(ImouConstant.DELETE_DEVICE_URL, JSON.toJSONString(map));
            logger.info("删除大华设备,response:{}", response);
            Result result = parseResult(response);
            if (result == null || !ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                String msg = "删除大华设备失败";
                if (result != null && StringUtils.isNotEmpty(result.getMsg())) {
                    msg = result.getMsg();
                }
                throw new BizException(ResultEnum.FAILURE.getCode(), msg);
            }
        } catch (Exception e) {
            logger.error("删除大华设备失败", e);
            throw new BizException(ResultEnum.FAILURE.getCode(), "删除大华设备失败");
        }
    }


    /**
     * 获取设备截图
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     * @param channelNo    通道号
     * @return 设备截图
     */
    public String getSnapshotUrl(String token, String deviceSerial, Integer channelNo) {
        Map<String, Object> params = new HashMap<>(3);
        params.put("token", token);
        params.put("deviceId", deviceSerial);
        params.put("channelId", channelNo);
        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String response = HttpUtil.doPost(ImouConstant.DEVICE_SNAPSHOT_URL, JSON.toJSONString(map));
            logger.info("乐橙云抓取图片,response:{}", response);
            Result result = parseResult(response);
            if (result != null && ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                return ((JSONObject) result.getData()).getString("url");
            }
        } catch (Exception e) {
            logger.error("乐橙云抓取图片失败", e);
        }
        return null;
    }


    /**
     * 获取直播流地址
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     * @param channelNo    通道号
     * @return 直播流地址
     */
    public ImouStreamInfo getStreamUrl(String token, String deviceSerial, Integer channelNo) {
        Map<String, Object> params = new HashMap<>(3);
        params.put("token", token);
        params.put("deviceId", deviceSerial);
        params.put("channelId", channelNo);
        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String response = HttpUtil.doPost(ImouConstant.STREAM_INFO_URL, JSON.toJSONString(map));
            logger.info("获取直播流,response:{}", response);
            Result result = parseResult(response);
            if (result == null || !ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                String msg = "获取直播流失败";
                if (result != null && StringUtils.isNotEmpty(result.getMsg())) {
                    msg = result.getMsg();
                }
                throw new BizException(ResultEnum.FAILURE.getCode(), msg);
            }
            JSONArray jsonArray = ((JSONObject) result.getData()).getJSONArray("streams");
            if (jsonArray == null || jsonArray.isEmpty()) {
                throw new BizException(ResultEnum.FAILURE.getCode(), "获取直播地址失败.");
            }
            ImouStreamInfo streamInfoDTO = new ImouStreamInfo();
            for (Object obj : jsonArray) {
                JSONObject jsonObject = (JSONObject) obj;
                String hls = jsonObject.getString("hls");
                // 大华视频流状态 0:正在直播中,1:正在直播中，但是视频封面异常,2:视频源异常,3:码流转换异常,4:云存储访问异常,10:直播暂停中
                Integer status = jsonObject.getInteger("status");
                if (status == null) {
                    continue;
                }
                if (status != 0 && status != 1) {
                    continue;
                }
                //码流类型（0:主码流；1:辅码流）
                Integer streamId = jsonObject.getInteger("streamId");
                if (streamId != null) {
                    if (streamId == 0) {
                        streamInfoDTO.setHlsHd(hls);
                    } else if (streamId == 1) {
                        streamInfoDTO.setHls(hls);
                    }
                }
            }
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            //过期时间设为7天
            calendar.add(Calendar.DATE, 7);
            streamInfoDTO.setExpireTime(calendar.getTime());
            return streamInfoDTO;
        } catch (Exception e) {
            logger.error("获取直播地址失败", e);
        }
        return null;
    }

    /**
     * 获取回放播放地址
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     * @param channelNo    通道号
     * @param startTime    开始时间
     * @param endTime      结束时间
     * @return 回放播放地址
     */
    public String getRecordList(String token, String deviceSerial, Integer channelNo, Date startTime, Date endTime) {
        Map<String, Object> params = new HashMap<>(5);
        params.put("token", token);
        params.put("deviceId", deviceSerial);
        params.put("channelId", channelNo);
        params.put("beginTime", DateUtil.formatDateTime(startTime));
        params.put("endTime", DateUtil.formatDateTime(endTime));
        params.put("queryRange", "1-100");

        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String response = HttpUtil.doPost(ImouConstant.VIDEO_RECORD_URL, JSON.toJSONString(map));
            logger.info("获取回放记录,response:{}", response);
            Result result = parseResult(response);
            if (result == null || !ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                String msg = "获取回放记录失败";
                if (result != null && StringUtils.isNotEmpty(result.getMsg())) {
                    msg = result.getMsg();
                }
                throw new BizException(ResultEnum.FAILURE.getCode(), msg);
            }
            return ((JSONObject) result.getData()).getString("cloudRecordM3u8");
        } catch (Exception e) {
            logger.error("获取回放记录出错", e);
        }
        return null;
    }


    /**
     * 云台操作
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     * @param channelNo    通道号
     * @param operation    操作  0-上，1-下，2-左，3-右，4-左上，5-左下，6-右上，7-右下，8-放大，9-缩小，10-停止
     * @param duration     操作时长 单位毫秒
     */
    public void ptz(String token, String deviceSerial, Integer channelNo, Integer operation, Long duration) {
        Map<String, Object> params = new HashMap<>();
        params.put("token", token);
        params.put("deviceId", deviceSerial);
        params.put("channelId", channelNo);
        params.put("operation", operation);
        params.put("duration", duration);
        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String paramsStr = JSON.toJSONString(map);
            String response = HttpUtil.doPost(ImouConstant.DEVICE_PTZ_URL, paramsStr);
            logger.info("大华启动云台操作,response:{}", response);

            Result result = parseResult(response);
            check(result);
            if (result != null && ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                ptzStop(paramsStr);
            }
        } catch (BizException e) {
            throw e;
        } catch (Exception e) {
            logger.error("大华启动云台操作失败:" + e.getMessage(), e);
            throw new BizException(ResultEnum.FAILURE.getCode(), "操作失败");
        }
    }

    /**
     * 获取设备信息
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     * @param channelNo    通道号
     * @return 设备信息
     */
    public ImouDeviceInfo getDeviceInfo(String token, String deviceSerial, Integer channelNo) {
        Map<String, Object> params = new HashMap<>();
        params.put("token", token);
        params.put("deviceId", deviceSerial);
        params.put("channelId", channelNo);
        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String response = HttpUtil.doPost(ImouConstant.DEVICE_INFO_URL, JSON.toJSONString(map));
            logger.info("获取设备信息,response:{}", response);
            Result result = parseResult(response);
            if (result == null || !ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                String msg = "获取设备信息失败";
                if (result != null && StringUtils.isNotEmpty(result.getMsg())) {
                    msg = result.getMsg();
                }
                throw new BizException(ResultEnum.FAILURE.getCode(), msg);
            }
            JSONObject jsonObject = JSON.parseObject(result.getData().toString());
            if (jsonObject != null) {
                ImouDeviceInfo deviceInfo = new ImouDeviceInfo();
                int status = 0;
                String channelOnline = jsonObject.getString("channelOnline");
                if (StringUtils.isNotBlank(channelOnline) && "online".equals(channelOnline)) {
                    status = 1;
                }
                deviceInfo.setStatus(status);
                String channelAbility = jsonObject.getString("channelAbility");
                deviceInfo.setAbility(channelAbility);
                return deviceInfo;
            }
        } catch (Exception e) {
            logger.error("获取设备状态错误", e);
        }
        return null;
    }

    /**
     * 开启直播
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     * @param channelNo    通道号
     */
    public void startDevice(String token, String deviceSerial, Integer channelNo) {
        Map<String, Object> params = new HashMap<>();
        params.put("token", token);
        params.put("deviceId", deviceSerial);
        params.put("channelId", channelNo);
        params.put("streamId", 0);
        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String response = HttpUtil.doPost(ImouConstant.START_DEVICE_URL, JSON.toJSONString(map));
            logger.info("启动设备,response:{}", response);
        } catch (Exception e) {
            logger.error("启动设备失败", e);
        }
    }

    /**
     * 获取通道信息
     *
     * @param token        token
     * @param deviceSerial 设备序列号
     * @return 通道信息
     */
    public List<ImouChannelInfo> getChannelInfo(String token, String deviceSerial) {
        Map<String, Object> params = new HashMap<>(2);
        params.put("token", token);
        params.put("deviceIds", deviceSerial);
        Map<String, Object> map = paramsInit();
        map.put("params", params);
        try {
            String response = HttpUtil.doPost(ImouConstant.DEVICE_CHANNEL_URL, JSON.toJSONString(map));
            logger.info("获取通道信息,response:{}", response);
            Result result = parseResult(response);
            if (result != null && ImouConstant.REQUEST_SUCCESS_CODE.equals(result.getCode())) {
                JSONArray devices = ((JSONObject) result.getData()).getJSONArray("devices");
                Object obj = devices.get(0);
                if (obj != null) {
                    String channels = ((JSONObject) obj).getString("channels");
                    return JSON.parseArray(channels, ImouChannelInfo.class);
                }
            }
        } catch (Exception e) {
            logger.error("获取通道信息败", e);
        }
        return null;
    }


    /**
     * 停止云台操作
     *
     * @param params 停止云台请求参数
     */
    private void ptzStop(String params) {
        try {
            String response = HttpUtil.doPost(ImouConstant.DEVICE_PTZ_URL, params);
            logger.info("大华停止云台操作,response:{}", response);
        } catch (Exception e) {
            logger.error("大华停止云台操作失败", e);
        }
    }


    /**
     * 初始化获取Token的参数
     *
     * @return paramsMap
     */
    private Map<String, Object> paramsInit() {

        long time = System.currentTimeMillis() / 1000;
        String nonce = UUID.randomUUID().toString();
        String id = UUID.randomUUID().toString();

        StringBuilder paramString = new StringBuilder();
        paramString.append("time:").append(time).append(",");
        paramString.append("nonce:").append(nonce).append(",");
        paramString.append("appSecret:").append(appSecret);

        String sign = "";
        // 计算MD5得值
        try {
            System.out.println("传入参数：" + paramString.toString().trim());
            sign = DigestUtils.md5Hex(paramString.toString().trim().getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            logger.error("大华初始化请求参数失败", e);
        }

        Map<String, Object> systemMap = new HashMap<>(5);
        systemMap.put("ver", "1.0");
        systemMap.put("sign", sign);
        systemMap.put("appId", appKey);
        systemMap.put("nonce", nonce);
        systemMap.put("time", time);

        Map<String, Object> map = new HashMap<>(3);
        map.put("system", systemMap);
        map.put("id", id);
        return map;
    }

    /**
     * 处理返回结果
     *
     * @param response 响应体
     * @return 返回结果
     */
    private Result parseResult(String response) {
        if (StringUtils.isNotBlank(response)) {
            JSONObject jsonObject = JSON.parseObject(response);
            if (jsonObject != null) {
                String resultStr = jsonObject.getString("result");
                if (StringUtils.isNotBlank(resultStr)) {
                    return JSON.parseObject(resultStr, Result.class);
                }
            }
        }
        return null;
    }


    public static void main2(String[] args) {
        ImouApi imouApi = new ImouApi("lc4c974d2deb1d475e", "f8fb098cb917442da934b05eb3cd60");
        String token = imouApi.getAccessToken();
        String deviceSerial = "5J0673EPBZ13B38";
        imouApi.ptz(token, deviceSerial, 0, 1, 2000L);
    }

    private void check(Result result) {
        String msg = Optional.ofNullable(result).map(v -> v.getMsg()).orElse("操作失败");
        if (result == null || !"0".equals(result.getCode())) {
            throw new BizException(ResultEnum.FAILURE.getCode(), msg);
        }
    }

}
